﻿using Loong.Admin.Security.Cryptography;
using System;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;

namespace Loong.Admin.Users
{
    public class User : FullAuditedAggregateRoot<Guid>, IMultiTenant, IHasEntityVersion
    {
        public Guid? TenantId { get; protected set; }

        public string UserName { get; protected internal set; } = null!;

        [DisableAuditing]
        public string NormalizedUserName { get; protected internal set; } = null!;

        public string? Name { get; set; }

        public string? Surname { get; set; }

        public string Email { get; protected internal set; } = null!;

        [DisableAuditing]
        public string NormalizedEmail { get; protected internal set; } = null!;

        public bool EmailConfirmed { get; protected internal set; }

        [DisableAuditing]
        public string? PasswordHash { get; protected internal set; }

        /// <summary>
        /// A random value that must change whenever a users credentials change (password changed, login removed)
        /// </summary>
        [DisableAuditing]
        public string SecurityStamp { get; protected internal set; } = null!;

        public string? PhoneNumber { get; protected internal set; }

        public bool PhoneNumberConfirmed { get; protected internal set; }

        public bool IsActive { get; protected internal set; }

        public bool TwoFactorEnabled { get; protected internal set; }

        public DateTimeOffset? LockoutEnd { get; protected internal set; }

        public bool LockoutEnabled { get; protected internal set; }

        public int AccessFailedCount { get; protected internal set; }

        public bool ShouldChangePasswordOnNextLogin { get; protected internal set; }

        public DateTimeOffset? LastPasswordChangeTime { get; protected set; }

        public bool IsExternal { get; set; }

        public int EntityVersion { get; protected set; }

        protected User()
        {

        }

        public User(Guid id, string userName, string email, Guid? tenantId = null) : base(id)
        {
            Check.NotNull(userName, nameof(userName));
            Check.NotNull(email, nameof(email));

            TenantId = tenantId;
            UserName = userName;
            NormalizedUserName = userName.ToUpperInvariant();
            Email = email;
            NormalizedEmail = email.ToUpperInvariant();
            SecurityStamp = Guid.NewGuid().ToString();
            IsActive = true;
        }

        public void SetEmail(string email)
        {
            Check.NotNull(email, nameof(email));

            Email = email;
            NormalizedEmail = email.ToUpperInvariant();
            SetEmailConfirmed(false);
            SetSecurityStamp();
        }

        public void SetEmailConfirmed(bool confirmed)
        {
            EmailConfirmed = confirmed;
        }

        public void SetPhoneNumber(string phoneNumber)
        {
            Check.NotNull(phoneNumber, nameof(phoneNumber));

            PhoneNumber = phoneNumber;
            SetPhoneNumberConfirmed(false);
            SetSecurityStamp();
        }

        public void SetPhoneNumberConfirmed(bool confirmed)
        {
            PhoneNumberConfirmed = confirmed;
        }

        public void SetShouldChangePasswordOnNextLogin(bool shouldChangePasswordOnNextLogin)
        {
            ShouldChangePasswordOnNextLogin = shouldChangePasswordOnNextLogin;
        }

        public void SetLastPasswordChangeTime(DateTimeOffset? lastPasswordChangeTime)
        {
            LastPasswordChangeTime = lastPasswordChangeTime;
        }

        public void SetLockoutEnabled(bool enabled)
        {
            LockoutEnabled = enabled;
        }

        public void IncrementAccessFailedCount()
        {
            AccessFailedCount++;
        }

        public void ResetAccessFailedCount()
        {
            AccessFailedCount = 0;
        }

        public void SetLockoutEnd(DateTimeOffset? lockoutEnd)
        {
            LockoutEnd= lockoutEnd;
        }

        public bool IsLockedOut(DateTime now)
        {
            return LockoutEnabled && LockoutEnd >= now;
        }

        public void SetIsActive(bool isActive)
        {
            IsActive = isActive;
        }

        public void SetSecurityStamp()
        {
            SecurityStamp = NewSecurityStamp();
        }

        private static string NewSecurityStamp()
        {
            return Base32.GenerateBase32();
        }

        public override string ToString()
        {
            return $"{base.ToString()}, UserName = {UserName}";
        }
    }
}
